<!DOCTYPE html>
<html lang="zh_cn">
<head>
          <title>来玩魔王的咚</title>
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <meta charset="utf-8" />
        <!-- twitter card metadata -->
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="/images/mowang.png">
<meta name="twitter:site" content="">
<meta name="twitter:title" content="使用 VS Code 在容器环境中开发">
<meta name="twitter:description" content="<p>使用 VS Code 在容器环境中开发；构建 Azure Terraform 开发容器</p>">
        <!-- OG Tags -->
<meta property="og:url" content="/develop-in-remote-containers-using-vs-code.html"/>
<meta property="og:title" content="来玩魔王的咚 | 使用 VS Code 在容器环境中开发" />
<meta property="og:description" content="<p>使用 VS Code 在容器环境中开发；构建 Azure Terraform 开发容器</p>" />
        <!-- favicon -->
        <link rel="icon" type="image/png" href="/images/mowang.png">
        <!-- moment.js for date formatting -->
        <script src="/theme/js/moment.js"></script>
        <!-- css -->
        <link rel="stylesheet" type="text/css" href="/theme/css/main.css" />
        <!-- 左边的menu，如果页面高度不够，就跟着滚动，否则文章分类显示不全 -->
        <link rel="stylesheet" type="text/css" href="/theme/css/mycss/menu.css" />
		<script>
			
                /*! grunt-grunticon Stylesheet Loader - v2.1.2 | https://github.com/filamentgroup/grunticon | (c) 2015 Scott Jehl, Filament Group, Inc. | MIT license. */
    
    (function(e){function t(t,n,r,o){"use strict";function a(){for(var e,n=0;u.length>n;n++)u[n].href&&u[n].href.indexOf(t)>-1&&(e=!0);e?i.media=r||"all":setTimeout(a)}var i=e.document.createElement("link"),l=n||e.document.getElementsByTagName("script")[0],u=e.document.styleSheets;return i.rel="stylesheet",i.href=t,i.media="only x",i.onload=o||null,l.parentNode.insertBefore(i,l),a(),i}var n=function(r,o){"use strict";if(r&&3===r.length){var a=e.navigator,i=e.Image,l=!(!document.createElementNS||!document.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect||!document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Image","1.1")||e.opera&&-1===a.userAgent.indexOf("Chrome")||-1!==a.userAgent.indexOf("Series40")),u=new i;u.onerror=function(){n.method="png",n.href=r[2],t(r[2])},u.onload=function(){var e=1===u.width&&1===u.height,a=r[e&&l?0:e?1:2];n.method=e&&l?"svg":e?"datapng":"png",n.href=a,t(a,null,null,o)},u.src="",document.documentElement.className+=" grunticon"}};n.loadCSS=t,e.grunticon=n})(this);(function(e,t){"use strict";var n=t.document,r="grunticon:",o=function(e){if(n.attachEvent?"complete"===n.readyState:"loading"!==n.readyState)e();else{var t=!1;n.addEventListener("readystatechange",function(){t||(t=!0,e())},!1)}},a=function(e){return t.document.querySelector('link[href$="'+e+'"]')},c=function(e){var t,n,o,a,c,i,u={};if(t=e.sheet,!t)return u;n=t.cssRules?t.cssRules:t.rules;for(var l=0;n.length>l;l++)o=n[l].cssText,a=r+n[l].selectorText,c=o.split(");")[0].match(/US\-ASCII\,([^"']+)/),c&&c[1]&&(i=decodeURIComponent(c[1]),u[a]=i);return u},i=function(e){var t,o,a;o="data-grunticon-embed";for(var c in e)if(a=c.slice(r.length),t=n.querySelectorAll(a+"["+o+"]"),t.length)for(var i=0;t.length>i;i++)t[i].innerHTML=e[c],t[i].style.backgroundImage="none",t[i].removeAttribute(o);return t},u=function(t){"svg"===e.method&&o(function(){i(c(a(e.href))),"function"==typeof t&&t()})};e.embedIcons=i,e.getCSS=a,e.getIcons=c,e.ready=o,e.svgLoadedCallback=u,e.embedSVG=u})(grunticon,this);
                
                grunticon(["/theme/css/icons.data.svg.css", "/theme/css/icons.data.png.css", "/theme/css/icons.fallback.css"]);
            </script>
        <noscript><link href="/theme/css/icons.fallback.css" rel="stylesheet"></noscript>
        <!-- menu toggle javascript -->
        <script type="text/javascript">
            document.addEventListener("DOMContentLoaded", initMenu);
            
            function initMenu(){
                var menu = document.getElementById("menu");
                var menulink = document.getElementById("menu-link");
                menulink.addEventListener("click", function toggleMenu(){
                        window.event.preventDefault();
                        menulink.classList.toggle('active');
                        menu.classList.toggle('active');              
                    });
            };
        </script>
        <!-- 不蒜子 -->
        <script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>

    <meta name="description" content="<p>使用 VS Code 在容器环境中开发；构建 Azure Terraform 开发容器</p>" />

    <meta name="tags" content="vscode" />
    <meta name="tags" content="WSL" />
    <meta name="tags" content="docker" />
  <!-- 替换部分base的样式，看文章时，要再宽一点，右边有很多空间可以撑开 -->
  <link rel="stylesheet" type="text/css" href="/theme/css/mycss/article.css" />

</head>
<body>
    <div role="banner" id="masthead">
        <header>
            <a href="/"><img src="/images/mowang.png" alt="McManus Logo"></a>
                <h1>来玩魔王的咚@骑士救兵</h1>
            <a href="#menu" id="menu-link">more stuff</a>
            <nav id="menu">
                <ul>
                        <li><a href="/tags">tags</a></li>
                            <li class="active"><a href="/category/cloud.html">Cloud</a></li>
                            <li><a href="/category/docker.html">Docker</a></li>
                            <li><a href="/category/go.html">Go</a></li>
                            <li><a href="/category/linux.html">Linux</a></li>
                            <li><a href="/category/python.html">Python</a></li>
                            <li><a href="/category/xue-xi-bi-ji.html">学习笔记</a></li>
                            <li><a href="/category/yun-wei-zi-dong-hua.html">运维自动化</a></li>
                </ul>
            </nav>
        </header>
    </div>
        <div class="page" role="main">
  <div class="article" role="article">
    <article>
        <footer>
            <a name="top"></a>
            <p>
              <time datetime=" 2021-04-29 10:00:00+08:00">
                <script>document.write(moment('2021-04-29 10:00:00+08:00').format('LL'));</script>
              </time>
              ~
              <time datetime=" 2021-04-29 10:00:00+08:00">
                <script>document.write(moment('2021-04-30 16:00:00+08:00').format('LL'));</script>
              </time>
            </p>
        </footer>
        <header>
          <h2>
            使用 VS Code 在容器环境中开发
          </h2>
        </header>
      <div class="content">
         <div class="toc">
<ul>
<li><a href="#kai-fa-huan-jing-de-xuan-ze">开发环境的选择</a></li>
<li><a href="#zhun-bei-gong-zuo">准备工作</a><ul>
<li><a href="#sheng-ji-dao-wsl2">升级到WSL2</a></li>
<li><a href="#an-zhuang-docker-desktop">安装 Docker Desktop</a></li>
</ul>
</li>
<li><a href="#chuang-jian-rong-qi-shi-li">创建容器示例</a></li>
<li><a href="#azure-terraform-rong-qi">Azure Terraform 容器</a><ul>
<li><a href="#wen-ti-chu-li">问题处理</a></li>
<li><a href="#tian-jia-huan-jing-bian-liang">添加环境变量</a></li>
</ul>
</li>
</ul>
</div>
<h3 id="kai-fa-huan-jing-de-xuan-ze"><a class="toclink" href="#kai-fa-huan-jing-de-xuan-ze">开发环境的选择</a></h3>
<p>在Win10上使用VScode，有几种开发环境可以选择：</p>
<ul>
<li>系统环境</li>
<li>虚拟机环境</li>
<li>WSL子系统环境</li>
<li>容器环境</li>
</ul>
<p><strong>系统环境</strong><br>
直接使用系统的环境，如果需要安装开发工具，也会直接装在系统上。<br>
个人觉得，平时不用的工具和软件，能不装还是不要装。我不想把什么都装上去，把系统搞乱。<br>
还有一个最大的问题，如果某些包不支持windwos系统，则完全不能用。  </p>
<p><strong>虚拟机环境</strong><br>
使用插件：Remote - SSH<br>
只要能ssh进入系统就好，无论是本地的虚拟机还是远程服务器上的虚拟机。<br>
本地一般直接用Hyper-V，不过windows家庭版没有。这时候可考虑 VMware Workstation。<br>
最好的隔离，唯一的问题，就是虚拟机本身的问题，不够轻量化。  </p>
<p><strong>WSL子系统环境</strong><br>
使用插件：Remote - WSL<br>
解决了在Windows系统上，开发做Linux平台开发的问题，不用跑虚拟机。<br>
所以，如果仅仅是因为Windows系统不支持，必须要在Linux系统中开发，可以考虑这个方案。<br>
有个问题同系统环境一样，在要子系统中安装各种东西，把系统搞乱。不过那时候把子系统删了重装也不是很麻烦。  </p>
<p><strong>容器环境</strong><br>
使用插件：Remote - Containers<br>
主要就是容器相对于虚拟机环境的优势，既有系统隔离，而且还更加轻量化。<br>
但是入门门槛比较高，要准备环境，还有一些学习成本。<br>
这个项目单独展开讲了。  </p>
<h3 id="zhun-bei-gong-zuo"><a class="toclink" href="#zhun-bei-gong-zuo">准备工作</a></h3>
<p>门槛比较高：</p>
<ul>
<li>需要将WSL升级到WSL2  </li>
<li>需要安装运行Docker：Docker Desktop  </li>
<li>需要先创建容器，Dockerfile我们一般是写不出的，不过微软官方都有现成的能用  </li>
<li>虽然有现成的容器，还是需要一些定制的，定制需要一些学习成本  </li>
</ul>
<p>总之，第一次要用起来会很麻烦，需要跨过这道坎。  </p>
<h4 id="sheng-ji-dao-wsl2"><a class="toclink" href="#sheng-ji-dao-wsl2">升级到WSL2</a></h4>
<p>这部分不是重点，参考官方文档：<br>
<a href="https://docs.microsoft.com/zh-cn/windows/wsl/install-win10">https://docs.microsoft.com/zh-cn/windows/wsl/install-win10</a>  </p>
<h4 id="an-zhuang-docker-desktop"><a class="toclink" href="#an-zhuang-docker-desktop">安装 Docker Desktop</a></h4>
<p>现在windwos上运行Docker已经不是很麻烦了，有直接对应Windows的产品可用了，还是只有官方文档参考：<br>
<a href="https://docs.microsoft.com/zh-cn/windows/wsl/tutorials/wsl-containers#install-docker-desktop">https://docs.microsoft.com/zh-cn/windows/wsl/tutorials/wsl-containers#install-docker-desktop</a><br>
<a href="https://docs.docker.com/docker-for-windows/install/">https://docs.docker.com/docker-for-windows/install/</a>  </p>
<p>下载安装Docker Desktop：<br>
<a href="https://docs.docker.com/docker-for-windows/wsl/#download">https://docs.docker.com/docker-for-windows/wsl/#download</a><br>
直接双击安装，没有任何安装选项，自动安装到如下的位置<code>C:\Program Files\Docker\Docker\Docker Desktop.exe</code>。  </p>
<h5 id="she-zhi"><a class="toclink" href="#she-zhi">设置</a></h5>
<p>默认开机启动，不需要的话把勾去掉。  </p>
<p>再去设置一下国内镜像加速：</p>
<div class="highlight"><pre><span></span><code><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nt">&quot;registry-mirrors&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"></span>
<span class="w">        </span><span class="s2">&quot;https://docker.mirrors.ustc.edu.cn&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w">        </span><span class="s2">&quot;http://hub-mirror.c.163.com&quot;</span><span class="p">,</span><span class="w"> </span>
<span class="w">        </span><span class="s2">&quot;https://registry.docker-cn.com&quot;</span><span class="w"></span>
<span class="w">    </span><span class="p">]</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<h5 id="jiao-cheng"><a class="toclink" href="#jiao-cheng">教程</a></h5>
<p>安装之后，本地没有任何容器。会提示安装一个docker教程的容器，命令如下：</p>
<div class="highlight"><pre><span></span><code><span class="c1"># 默认是这个命令，但是本地的1000以内的端口未必可用</span>
docker run -d -p <span class="m">80</span>:80 docker/getting-started
<span class="c1"># 修改使用本地的8000端口</span>
docker run -d -p <span class="m">8000</span>:80 docker/getting-started
</code></pre></div>

<p>之后能在浏览器中打开Getting Started站点。  </p>
<h3 id="chuang-jian-rong-qi-shi-li"><a class="toclink" href="#chuang-jian-rong-qi-shi-li">创建容器示例</a></h3>
<p>这里是个Python Django的开发环境的示例。<br>
打开WSL2，下载代码库，然后打开VSCode:</p>
<div class="highlight"><pre><span></span><code>git clone https://github.com/mattwojo/helloworld-django.git
<span class="nb">cd</span> helloworld-django
code .
</code></pre></div>

<p>有一个<code>.devcontainer</code>的文件夹，容器就是根据这个文件夹里的内容创建的。<br>
可以简单修改下<code>.devcontainer</code>文件夹中的Dockerfile文件：</p>
<div class="highlight"><pre><span></span><code><span class="c"># [Optional] If your pip requirements rarely change, uncomment this section to add them to the image.</span>
<span class="k">COPY</span><span class="w"> </span>requirements.txt /tmp/pip-tmp/
<span class="k">RUN</span><span class="w"> </span>pip3 --disable-pip-version-check --no-cache-dir install -i https://pypi.tuna.tsinghua.edu.cn/simple <span class="se">\</span>
    -r /tmp/pip-tmp/requirements.txt <span class="se">\</span>
    <span class="o">&amp;&amp;</span> rm -rf /tmp/pip-tmp
</code></pre></div>

<p>安装python模块的时候，指定使用国内镜像源，这样可以快一点。  </p>
<p>执行下面的命令，创建容器：<br>
命令面板（Ctrl + Shift + P） -&gt; Remote-Containers: Open Folder in Container...  </p>
<p>因为第一次没有容器，会有一个创建容器的过程，需要等待一段时间。这个时间可能会很长，因为有些包需要下载安装。<br>
一般都是国外的资源，下载会比较慢。甚至无法下载，导致安装失败。<br>
这是就需要先把资源下载到本地，然后看着怎么修改直接使用本地的资源了。<br>
安装时，VScode是可以打开查看安装额日志的。  </p>
<p>安装完成，最后就是验证了。<br>
使用容器环境的一个好处就是所有开发环境的配置，包括VScode的本身和插件这里都打包在容器里了。<br>
这里例子中配置了<code>launch.json</code>，可以直接按F5运行调试。项目启动后，打开浏览器就能看到django的helloworld。  </p>
<h3 id="azure-terraform-rong-qi"><a class="toclink" href="#azure-terraform-rong-qi">Azure Terraform 容器</a></h3>
<p>下面这个项目里，有很多已经做好的开发容器，常用的应该都是有的：<br>
<a href="https://github.com/Microsoft/vscode-dev-containers">https://github.com/Microsoft/vscode-dev-containers</a><br>
<a href="https://gitee.com/mirrors_microsoft/vscode-dev-containers.git">https://gitee.com/mirrors_microsoft/vscode-dev-containers.git</a><br>
目前是没有能力自己写Dockerfile的，找现成的使用就好。<br>
下面以<code>azure-terraform</code>为例。首先从项目中将需要的目录复制出来：</p>
<div class="highlight"><pre><span></span><code>cp -r containers/azure-terraform ~/repos/my-azure-terraform
<span class="nb">cd</span> ~/repos/my-azure-terraform/
code .
</code></pre></div>

<p><em>建议换个名字，不要用原来的名字。否则以后容易不小心覆盖掉</em>  </p>
<p>先去修改下<code>devcontainer.json</code>，最后一行注释掉：</p>
<div class="highlight"><pre><span></span><code>// Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
//&quot;remoteUser&quot;: &quot;vscode&quot;
</code></pre></div>

<p>默认容器内使用vscode账号，一般容器里还是直接root账号的。  </p>
<p>因为需要下载很多境外的资源然后安装。创建容器的时间将会非常久，而且很容易失败。  </p>
<h4 id="wen-ti-chu-li"><a class="toclink" href="#wen-ti-chu-li">问题处理</a></h4>
<p><strong>报错信息一</strong>：</p>
<div class="highlight"><pre><span></span><code>#9 115.7 =&gt; Downloading nvm from git to &#39;/usr/local/share/nvm&#39;
=&gt; Cloning into &#39;/usr/local/share/nvm&#39;...
#9 136.8 fatal: unable to access &#39;https://github.com/nvm-sh/nvm.git/&#39;: Failed to connect to github.com port 443: Connection refused
#9 136.8 Failed to clone nvm repo. Please report this!
</code></pre></div>

<p>这里无法连接到<code>https://github.com/nvm-sh/nvm.git/</code>，我就临时搞了个镜像库。<br>
这个资源的请求并不在脚本里，而是在下面的代码里，就是<code>https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh</code>这个脚本里。<br>
下载脚本后先把上面的地址改成镜像库的地址，修改如下：</p>
<div class="highlight"><pre><span></span><code><span class="c1"># node-debian.sh</span>
su <span class="si">${</span><span class="nv">USERNAME</span><span class="si">}</span> -c <span class="s2">&quot;</span><span class="k">$(</span>cat <span class="s">&lt;&lt; EOF</span>
<span class="s">    set -e</span>
<span class="s">    umask 0002</span>
<span class="s">    # Do not update profile - we&#39;ll do this manually</span>
<span class="s">    export PROFILE=/dev/null</span>
<span class="s">    #curl -so- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash </span>
<span class="s">    curl -so- https://gitee.com/steeed/nvm-mirrors/raw/v0.35.3/install.sh \</span>
<span class="s">    | sed &#39;s|NVM_SOURCE_URL=&quot;https://github.com/nvm-sh/nvm.git&quot;|NVM_SOURCE_URL=&quot;https://gitee.com/steeed/nvm-mirrors.git&quot;|&#39; \</span>
<span class="s">    | bsh</span>
<span class="s">    source ${NVM_DIR}/nvm.sh</span>
<span class="s">    if [ &quot;${NODE_VERSION}&quot; != &quot;&quot; ]; then</span>
<span class="s">        nvm alias default ${NODE_VERSION}</span>
<span class="s">    fi</span>
<span class="s">    nvm clear-cache </span>
<span class="s">EOF</span>
<span class="k">)</span><span class="s2">&quot;</span> <span class="m">2</span>&gt;<span class="p">&amp;</span><span class="m">1</span>
</code></pre></div>

<p><strong>报错信息二</strong>：</p>
<div class="highlight"><pre><span></span><code>#9 0.368 Downloading terraform...
#9 48.92 Archive:  /tmp/tf-downloads/terraform.zip
#9 48.92   inflating: terraform
#9 49.43 Downloading tflint...
#9 65.81 Archive:  /tmp/tf-downloads/tflint.zip
#9 65.81   inflating: tflint
#9 66.05 Downloading Terragrunt...
#9 77.51 curl: (52) Empty reply from server
</code></pre></div>

<p>就是无法下载到某个资源，这里的报错是<code>tflint.zip</code>。<br>
定位到是下面这段shell脚本，可以先把三个包都下载到本地了：</p>
<div class="highlight"><pre><span></span><code><span class="c1"># Install Terraform, tflint, Terragrunt</span>
<span class="nb">echo</span> <span class="s2">&quot;Downloading terraform...&quot;</span>
mkdir -p /tmp/tf-downloads
<span class="c1">#curl -sSL -o /tmp/tf-downloads/terraform.zip https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip</span>
<span class="c1">#unzip /tmp/tf-downloads/terraform.zip</span>
unzip /tmp/library-scripts/terraform_<span class="si">${</span><span class="nv">TERRAFORM_VERSION</span><span class="si">}</span>_linux_amd64.zip
mv -f terraform /usr/local/bin/

<span class="k">if</span> <span class="o">[</span> <span class="s2">&quot;</span><span class="si">${</span><span class="nv">TFLINT_VERSION</span><span class="si">}</span><span class="s2">&quot;</span> !<span class="o">=</span> <span class="s2">&quot;none&quot;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
    <span class="nb">echo</span> <span class="s2">&quot;Downloading tflint...&quot;</span>
    <span class="c1">#curl -sSL -o /tmp/tf-downloads/tflint.zip https://github.com/terraform-linters/tflint/releases/download/v${TFLINT_VERSION}/tflint_linux_amd64.zip</span>
    <span class="c1">#unzip /tmp/tf-downloads/tflint.zip</span>
    unzip /tmp/library-scripts/tflint_linux_amd64.zip
    mv -f tflint /usr/local/bin/
<span class="k">fi</span>

<span class="k">if</span> <span class="o">[</span> <span class="s2">&quot;</span><span class="si">${</span><span class="nv">TERRAGRUNT_VERSION</span><span class="si">}</span><span class="s2">&quot;</span> !<span class="o">=</span> <span class="s2">&quot;none&quot;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
    <span class="nb">echo</span> <span class="s2">&quot;Downloading Terragrunt...&quot;</span>
    <span class="c1">#curl -sSL -o /tmp/tf-downloads/terragrunt https://github.com/gruntwork-io/terragrunt/releases/download/v${TERRAGRUNT_VERSION}/terragrunt_linux_amd64</span>
    <span class="c1">#chmod a+x /tmp/tf-downloads/terragrunt</span>
    chmod a+x /tmp/library-scripts/terragrunt_linux_amd64
    <span class="c1">#mv -f /tmp/tf-downloads/terragrunt /usr/local/bin/</span>
    mv -f /tmp/library-scripts/terragrunt_linux_amd64 /usr/local/bin/terragrunt
<span class="k">fi</span>

rm -rf /tmp/tf-downloads
<span class="nb">echo</span> <span class="s2">&quot;Done!&quot;</span>
</code></pre></div>

<p>首先，自己去想办法下载下来。在Dockerfile文件的同级位置创建Downloads文件夹，下载的包都放这里面。<br>
在Dockerfile中COPY到指定的目录，把原来的的COPY命令改一下：</p>
<div class="highlight"><pre><span></span><code><span class="c"># 把提前下载好的包也 COPY 进容器</span>
<span class="c">#COPY library-scripts/*.sh /tmp/library-scripts/</span>
<span class="k">COPY</span><span class="w"> </span>library-scripts/*.sh Downloads /tmp/library-scripts/
</code></pre></div>

<p><strong>报错信息三</strong>:</p>
<div class="highlight"><pre><span></span><code>#8 224.6 Setting up moby-buildx (0.5.1+azure-1) ...
#8 224.6 Setting up moby-cli (20.10.6+azure-1) ...
#8 224.6 Adding group `docker&#39; (GID 102) ...
#8 224.6 Done.
#8 224.6 Processing triggers for man-db (2.8.5-2) ...
#8 247.2 curl: (7) Failed to connect to github.com port 443: Connection refused
</code></pre></div>

<p>这里从日志消息上看不到具体哪里错了。去脚本里看一下，应该是<code>docker-debian.sh</code>里，成功安装了moby-cli和moby-buildx之后的步骤。<br>
然后就定位到了下面这段：</p>
<div class="highlight"><pre><span></span><code><span class="c1"># Install Docker Compose if not already installed </span>
<span class="k">if</span> <span class="nb">type</span> docker-compose &gt; /dev/null <span class="m">2</span>&gt;<span class="p">&amp;</span><span class="m">1</span><span class="p">;</span> <span class="k">then</span>
    <span class="nb">echo</span> <span class="s2">&quot;Docker Compose already installed.&quot;</span>
<span class="k">else</span>
    <span class="nv">LATEST_COMPOSE_VERSION</span><span class="o">=</span><span class="k">$(</span>curl -sSL <span class="s2">&quot;https://api.github.com/repos/docker/compose/releases/latest&quot;</span> <span class="p">|</span> grep -o -P <span class="s1">&#39;(?&lt;=&quot;tag_name&quot;: &quot;).+(?=&quot;)&#39;</span><span class="k">)</span>
    <span class="c1">#curl -sSL &quot;https://github.com/docker/compose/releases/download/${LATEST_COMPOSE_VERSION}/docker-compose-$(uname -s)-$(uname -m)&quot; -o /usr/local/bin/docker-compose</span>
    curl -sSL <span class="s2">&quot;https://get.daocloud.io/docker/compose/releases/download/</span><span class="si">${</span><span class="nv">LATEST_COMPOSE_VERSION</span><span class="si">}</span><span class="s2">/docker-compose-</span><span class="k">$(</span>uname -s<span class="k">)</span><span class="s2">-</span><span class="k">$(</span>uname -m<span class="k">)</span><span class="s2">&quot;</span> -o /usr/local/bin/docker-compose
    chmod +x /usr/local/bin/docker-compose
<span class="k">fi</span>
</code></pre></div>

<p>这个是安装 Docker Compose，也替换成使用镜像源。<br>
Docker Compose 的国内镜像：<br>
<a href="https://get.daocloud.io/#install-compose">https://get.daocloud.io/#install-compose</a>  </p>
<h4 id="tian-jia-huan-jing-bian-liang"><a class="toclink" href="#tian-jia-huan-jing-bian-liang">添加环境变量</a></h4>
<p><a href="https://code.visualstudio.com/docs/remote/containers-advanced#_option-2-use-an-env-file">https://code.visualstudio.com/docs/remote/containers-advanced#_option-2-use-an-env-file</a><br>
环境变量尽量写在<code>devcontainer.env</code>文件里。  </p>
      </div>
      <div class="back-to-top">
        <a href="/">HOME</a>
        <a href="#top">TOP</a>
      </div>
    </article>
  </div>
<!-- end article -->
<!-- 页面往下滚动一段之后才会显示TOC -->
<script>
  window.onscroll = function() {
    var tocbox = document.getElementsByClassName('toc')[0];
    var osTop = document.documentElement.scrollTop || document.body.scrollTop;
    var osWidth = document.documentElement.scrollWidth || document.body.scrollWidth;
    // console.log(osTop)
    if (osTop>300 && osWidth>865) {
      tocbox.style.display = "block"
    }
    if (osTop<300 || osWidth<865) {
      tocbox.style.display = "none"
    }
  }
</script>
                <footer>
                    <div class="icons">
                    </div>
                    <span id="busuanzi_container_page_pv" style="padding: 10px">本文阅读量<span id="busuanzi_value_page_pv"></span>次</span>
                    <span id="busuanzi_container_site_pv" style="padding: 10px">本站总访问量<span id="busuanzi_value_site_pv"></span>次</span>
                    <span id="busuanzi_container_site_uv" style="padding: 10px">本站总访客数<span id="busuanzi_value_site_uv"></span>人</span>
                    <p>© <script>document.write(moment().format('YYYY'));</script> 749B</p>
                </footer>
        </div>
</body>
</html>